package com.rd.animation.type;

import com.rd.animation.controller.ValueController;
import com.rd.animation.data.type.DropAnimationValue;
import com.rd.utils.AnimatorUtils;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;

public class DropAnimation extends BaseAnimation<AnimatorGroup> {
    private int widthStart;
    private int widthEnd;
    private int heightStart;
    private int heightEnd;
    private int radius;
    private int fromRadius;
    private int toRadius;

    public enum AnimationType {
        Width,
        Height,
        Radius
    }

    private DropAnimationValue value;

    public DropAnimation(ValueController.UpdateListener listener) {
        super(listener);
        value = new DropAnimationValue();
    }

    @Override
    public AnimatorGroup createAnimator() {
        AnimatorGroup animator = new AnimatorGroup();
        animator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        //        animator.setInterpolator(new AccelerateDecelerateInterpolator());

        return animator;
    }

    @Override
    public DropAnimation progress(float progress) {
        if (animator != null) {
            long playTimeLeft = (long) (progress * animationDuration);
            boolean isReverse = false;

            for (Animator anim : animator.getAnimatorsAt(0)) {
                ValueAnimator animator = (ValueAnimator) anim;
                long animDuration = animator.getDuration();
                long currPlayTime = playTimeLeft;

                if (isReverse) {
                    currPlayTime -= animDuration;
                }

                if (currPlayTime < 0) {
                    continue;

                } else if (currPlayTime >= animDuration) {
                    currPlayTime = animDuration;
                }

                animator.onUpdate(animator, AnimatorUtils.getInterpolation(progress));
                onAnimatorUpdate(animator, ((DropAnimationValue) animator.getValue()).getType());
                //                if (animator.getValues() != null && animator.getValues().length > 0) {
                //                    animator.setCurrentPlayTime(currPlayTime);
                //                }

                if (!isReverse && animDuration >= animationDuration) {
                    isReverse = true;
                }
            }
        }

        return this;
    }

    @Override
    public DropAnimation duration(long duration) {
        super.duration(duration);
        return this;
    }

    @SuppressWarnings("UnnecessaryLocalVariable")
    public DropAnimation with(int widthStart, int widthEnd, int heightStart, int heightEnd, int radius) {
        if (hasChanges(widthStart, widthEnd, heightStart, heightEnd, radius)) {
            animator = createAnimator();

            this.widthStart = widthStart;
            this.widthEnd = widthEnd;
            this.heightStart = heightStart;
            this.heightEnd = heightEnd;
            this.radius = radius;

            fromRadius = radius;
            toRadius = (int) (radius / 1.5);
            long halfDuration = animationDuration / 2;

            ValueAnimator widthAnimator =
                    createValueAnimation(widthStart, widthEnd, animationDuration, AnimationType.Width);
            widthAnimator.setInnerListener(
                    new InnerValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float f) {
                            width = AnimatorUtils.getIntValue(widthStart, widthEnd, f);
                        }
                    });
            ValueAnimator heightForwardAnimator =
                    createValueAnimation(heightStart, heightEnd, halfDuration, AnimationType.Height);
            heightForwardAnimator.setInnerListener(
                    new InnerValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float f) {
                            height = AnimatorUtils.getIntValue(heightStart, heightEnd, f);
                        }
                    });
            ValueAnimator radiusForwardAnimator =
                    createValueAnimation(fromRadius, toRadius, halfDuration, AnimationType.Radius);
            radiusForwardAnimator.setInnerListener(
                    new InnerValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float f) {
                            radiusValue = AnimatorUtils.getIntValue(fromRadius, toRadius, f);
                        }
                    });
            ValueAnimator heightBackwardAnimator =
                    createValueAnimation(heightEnd, heightStart, halfDuration, AnimationType.Height);
            heightBackwardAnimator.setInnerListener(
                    new InnerValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float f) {
                            height = AnimatorUtils.getIntValue(heightEnd, heightStart, f);
                        }
                    });
            ValueAnimator radiusBackwardAnimator =
                    createValueAnimation(toRadius, fromRadius, halfDuration, AnimationType.Radius);
            radiusBackwardAnimator.setInnerListener(
                    new InnerValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float f) {
                            radiusValue = AnimatorUtils.getIntValue(toRadius, fromRadius, f);
                        }
                    });

            AnimatorGroup group1 = new AnimatorGroup();
            group1.runParallel(heightForwardAnimator, radiusForwardAnimator, widthAnimator);
            AnimatorGroup group2 = new AnimatorGroup();
            group2.runParallel(heightBackwardAnimator, radiusBackwardAnimator);
            animator.runSerially(group1, group2);
        }

        return this;
    }

    private ValueAnimator createValueAnimation(int fromValue, int toValue, long duration, final AnimationType type) {
        ValueAnimator anim = new ValueAnimator();
        anim.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        anim.setDuration(duration);
        DropAnimationValue value = new DropAnimationValue();

        value.setType(type);
        anim.setValue(value);
        anim.setValueUpdateListener(
                new AnimatorValue.ValueUpdateListener() {
                    @Override
                    public void onUpdate(AnimatorValue animatorValue, float v) {
                        anim.onUpdate(animatorValue, v);
                        onAnimatorUpdate(animatorValue, type);
                    }
                });

        return anim;
    }

    // 这里使用三个成员变量过度，后续看OS是否更新
    int width;
    int height;
    int radiusValue;

    private void onAnimatorUpdate(AnimatorValue animation, AnimationType type) {
        switch (type) {
            case Width:
                value.setWidth(width);
                break;

            case Height:
                value.setHeight(height);
                break;

            case Radius:
                value.setRadius(radiusValue);
                break;
        }

        if (listener != null) {
            listener.onValueUpdated(value);
        }
    }

    @SuppressWarnings("RedundantIfStatement")
    private boolean hasChanges(int widthStart, int widthEnd, int heightStart, int heightEnd, int radius) {
        if (this.widthStart != widthStart) {
            return true;
        }

        if (this.widthEnd != widthEnd) {
            return true;
        }

        if (this.heightStart != heightStart) {
            return true;
        }

        if (this.heightEnd != heightEnd) {
            return true;
        }

        if (this.radius != radius) {
            return true;
        }

        return false;
    }
}
